package product

import (
	"context"
	// "fmt"
	"strconv"
	"strings"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/common/moneyx"
	"mall/common/resultx"
	"mall/common/utils/pointy"
	"mall/service/product/model"
	"mall/service/product/rpc/internal/svc"
	"mall/service/product/rpc/types/product"

	// "github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type GetProductByIdLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewGetProductByIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetProductByIdLogic {
	return &GetProductByIdLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *GetProductByIdLogic) GetProductById(in *product.BaseIdReq) (*product.ProductInfo, error) {
	if (in.Filter == nil || len(*in.Filter) == 0) && (in.Id == nil || *in.Id == 0) {
		return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
	}

	preloadMap, eagerLoadMap := modelx.ParsePreloadAndEagerLoad(in.Preload, in.EagerLoad)

	// logrus.Info(fmt.Sprintf("GetProductById preloadMap: %+v", preloadMap))
	// map[product:[Id ParentId Title Slug Excerpt] productAttribute:[Id IsSku Name Slug HasImage] productDetail:[ContentHtml] productMedia:[Uid Thumbnail Order Url altText metaData] productMeta:[mediaId mediaUrl retailPriceMin scoreMin hotSell showStock stock stockUnit] productRelationship:[PerfumeId PerfumeName PerfumeSlug BrandId BrandName BrandSlug] productTerm:[Id Name Slug ParentId] productVariation:[Id RetailPrice] productVariationMeta:[stock mediaId mediaUrl sku skuKey]]

	// logrus.Info(fmt.Sprintf("GetProductById eagerLoadMap: %+v", eagerLoadMap))
	// map[product:[productDetail productMedia productRelationship productTerm productMeta productAttribute productVariation productVariationMeta]]

	productEntityName := model.RpcEntityNames.Product
	if _, ok := model.RpcEntityPreloadMap[productEntityName]; !ok {
		return &product.ProductInfo{}, nil
	}

	productRepositoryName := model.RepositoryNames.Product
	columnMap, ok := model.RepositoryPreloadMap[productRepositoryName]
	if !ok {
		return &product.ProductInfo{}, nil
	}

	var goods *model.ProductProduct
	if in.Filter != nil && len(*in.Filter) > 0 {
		var selectCols []string
		if preloadFields, ok := preloadMap[productEntityName]; ok {
			selectCols = modelx.ParseSelectColumns(preloadFields, columnMap)
		}
		selectCols = append(selectCols, model.ProductProductColumns.Status)

		filterMap := modelx.ParseFilter(in.Filter, columnMap)
		// logrus.Info(fmt.Sprintf("GetProductById filterMap: %+v", filterMap))
		// map[virtual_type:map[equalTo:[0]]]
		if filterMap == nil {
			return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
		}

		if in.Id != nil && *in.Id > 0 {
			filterMap[model.ProductProductColumns.ID] = map[string][]any{
				"equalTo": {*in.Id},
			}
		}

		orderBy := modelx.ParseSorter(in.Sorter, columnMap)
		// logrus.Info(fmt.Sprintf("GetProductById orderBy: %+v", orderBy))

		res, _, err := l.svcCtx.CurrentProduct.GetList(l.ctx, model.M{
			"pageSize": uint64(1),
			"orderBy":  orderBy,
			"select":   strings.Join(selectCols, ","),
		}, -1, filterMap)
		if err != nil {
			return nil, err
		}

		if len(res) == 0 {
			return &product.ProductInfo{}, nil
		}

		goods = res[0]
	} else {
		data, err := l.svcCtx.CurrentProduct.FindOne(l.ctx, *in.Id)
		if err != nil {
			return nil, err
		}

		goods = data
	}

	if goods == nil {
		return &product.ProductInfo{}, nil
	}

	if goods.Status != uint16(globalkey.PostStatusPublish) {
		return &product.ProductInfo{}, nil
	}

	pid := goods.ID
	subEntitySettingMap := make(map[string]map[string]*model.ColumnSetting)
	if eagerLoadEntities, ok := eagerLoadMap[productEntityName]; ok {
		for _, entity := range eagerLoadEntities {
			if entitySettingMap, ok := model.RpcEntityPreloadMap[entity]; ok {
				subEntitySettingMap[entity] = entitySettingMap
			}
		}
	}

	productDetailEntityName := model.RpcEntityNames.ProductDetail
	var content *string
	var tracing *string
	if _, ok := subEntitySettingMap[productDetailEntityName]; ok {
		productDetailRepositoryName := model.RepositoryNames.ProductDetail
		subPreloadFields, hasSubPreloadFields := preloadMap[productDetailEntityName]
		subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[productDetailRepositoryName]
		if hasSubPreloadFields && hasSubColumnMap {
			subSelectCols := modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.ProductDetailColumns.ProductID] = map[string][]any{
				"equalTo": {pid},
			}
			list, _, err := l.svcCtx.CurrentProduct.GetDetailList(l.ctx, model.M{
				"pageSize": uint64(1),
				"select":   strings.Join(subSelectCols, ","),
			}, -1, subFilterMap)
			if err != nil {
				return nil, err
			}

			if len(list) > 0 {
				content = pointy.GetPointer(list[0].Content)
				if list[0].Tracing.Valid {
					tracing = pointy.GetPointer(list[0].Tracing.String)
				}
			}
		}
	}

	productTermEntityName := model.RpcEntityNames.ProductTerm
	var categories []*product.TermInfo
	var tags []*product.TermInfo
	if _, ok := subEntitySettingMap[productTermEntityName]; ok {
		productTermRepositoryName := model.RepositoryNames.ProductTerm
		subPreloadFields, hasSubPreloadFields := preloadMap[productTermEntityName]
		subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[productTermRepositoryName]
		if hasSubPreloadFields && hasSubColumnMap {
			subSelectCols := modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.ProductTermRelationshipColumns.ObjectID] = map[string][]any{
				"equalTo": {pid},
			}
			subFilterMap[model.ProductTermRelationshipColumns.ObjectType] = map[string][]any{
				"equalTo": {globalkey.PostTypeProduct},
			}
			list, _, err := l.svcCtx.CurrentProduct.GetRelationshipList(l.ctx, model.M{
				"select": model.ProductTermRelationshipColumns.TermID,
			}, -1, subFilterMap)
			if err != nil {
				return nil, err
			}

			if len(list) > 0 {
				var tids []any
				for _, v := range list {
					tids = append(tids, v.TermID)
				}

				termFilterMap := make(map[string]map[string][]any)
				termFilterMap[model.ProductTermColumns.TermID] = map[string][]any{
					"in": tids,
				}
				subSelectCols = append(subSelectCols, model.ProductTermColumns.Taxonomy) // 必须获取以区分category及tag
				termList, _, err := l.svcCtx.ProductTerm.GetList(l.ctx, model.M{
					"select": strings.Join(subSelectCols, ","),
				}, -1, termFilterMap)
				if err != nil {
					return nil, err
				}

				for _, v := range termList {
					switch int32(v.Taxonomy) {
					case globalkey.TermTaxonomyCategory:
						categories = append(categories, &product.TermInfo{
							Id:       pointy.GetPointer(v.TermID),
							Name:     pointy.GetPointer(v.Name),
							Slug:     pointy.GetPointer(v.Slug),
							ParentId: pointy.GetPointer(v.ParentID),
						})
					case globalkey.TermTaxonomyProductTag:
						tags = append(tags, &product.TermInfo{
							Id:   pointy.GetPointer(v.TermID),
							Name: pointy.GetPointer(v.Name),
							Slug: pointy.GetPointer(v.Slug),
						})
					}
				}
			}
		}
	}

	productRelationshipEntityName := model.RpcEntityNames.ProductRelationship
	var entries []*product.EntryInfo
	if _, ok := subEntitySettingMap[productRelationshipEntityName]; ok {
		productRelationshipRepositoryName := model.RepositoryNames.ProductRelationship
		subPreloadFields, hasSubPreloadFields := preloadMap[productRelationshipEntityName]
		subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[productRelationshipRepositoryName]
		if hasSubPreloadFields && hasSubColumnMap {
			subSelectCols := modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.ProductKnowledgeBaseRelationshipColumns.ProductID] = map[string][]any{
				"equalTo": {pid},
			}
			list, _, err := l.svcCtx.CurrentProduct.GetEntryList(l.ctx, model.M{
				"select": strings.Join(subSelectCols, ","),
			}, -1, subFilterMap)
			if err != nil {
				return nil, err
			}

			if len(list) > 0 {
				for _, v := range list {
					if v.ToEntryID > 0 {
						entries = append(entries, &product.EntryInfo{
							Id:   pointy.GetPointer(v.ToEntryID),
							Key:  pointy.GetPointer(globalkey.ProductRelationNamePerfume),
							Name: pointy.GetPointer(v.ToEntryName),
							Slug: pointy.GetPointer(v.ToEntrySlug),
						})
					}
					if v.ToEntryGroupID > 0 {
						entries = append(entries, &product.EntryInfo{
							Id:   pointy.GetPointer(v.ToEntryGroupID),
							Key:  pointy.GetPointer(globalkey.ProductRelationNameBrand),
							Name: pointy.GetPointer(v.ToEntryGroupName),
							Slug: pointy.GetPointer(v.ToEntryGroupSlug),
						})
					}
				}
			}
		}
	}

	productAttributeEntityName := model.RpcEntityNames.ProductAttribute
	var attrs []*product.ProductAttrInfo
	var specs []*product.ProductAttrInfo
	if _, ok := subEntitySettingMap[productAttributeEntityName]; ok {
		productAttributeRepositoryName := model.RepositoryNames.ProductAttribute
		subPreloadFields, hasSubPreloadFields := preloadMap[productAttributeEntityName]
		subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[productAttributeRepositoryName]
		if hasSubPreloadFields && hasSubColumnMap {
			subSelectCols := modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.ProductAttributeColumns.ProductID] = map[string][]any{
				"equalTo": {pid},
			}
			var orderBy string = model.ProductAttributeColumns.ProductAttributeID
			list, _, err := l.svcCtx.CurrentProduct.GetAttributeList(l.ctx, model.M{
				"orderBy": orderBy,
				"select":  strings.Join(subSelectCols, ","),
			}, -1, subFilterMap)
			if err != nil {
				return nil, err
			}

			if len(list) > 0 {
				var ids []any
				for _, v := range list {
					ids = append(ids, v.ProductAttributeID)
				}

				optionFilterMap := make(map[string]map[string][]any)
				optionFilterMap[model.ProductAttributeOptionColumns.AttributeID] = map[string][]any{
					"in": ids,
				}
				// optionFilterMap[model.ProductAttributeOptionColumns.Status] = map[string][]any{
				// 	"equalTo": {globalkey.StatusEnabled},
				// }
				optionList, _, err := l.svcCtx.CurrentProduct.GetAttributeOptionList(l.ctx, model.M{}, -1, optionFilterMap)
				if err != nil {
					return nil, err
				}

				attrOptMap := make(map[uint64][]*product.ProductAttrOptInfo)
				if len(optionList) > 0 {
					for _, v := range optionList {
						attrOptMap[v.AttributeID] = append(attrOptMap[v.AttributeID], &product.ProductAttrOptInfo{
							Id:       pointy.GetPointer(v.OptionID),
							Label:    pointy.GetPointer(v.Name),
							Slug:     pointy.GetPointer(v.Slug),
							MediaId:  pointy.GetPointer(strconv.FormatInt(int64(v.ThumbnailID), 10)),
							MediaUrl: pointy.GetPointer(v.ThumbnailURL),
						})
					}
				}

				for _, v := range list {
					if opts, ok := attrOptMap[v.ProductAttributeID]; ok {
						if v.IsVariationAttribute > 0 {
							specs = append(specs, &product.ProductAttrInfo{
								Id:       pointy.GetPointer(v.ProductAttributeID),
								Label:    pointy.GetPointer(v.Name),
								Slug:     pointy.GetPointer(v.Slug),
								HasMedia: pointy.GetPointer(uint32(v.HasImage)),
								Options:  opts,
							})
						} else {
							attrs = append(attrs, &product.ProductAttrInfo{
								Id:       pointy.GetPointer(v.ProductAttributeID),
								Label:    pointy.GetPointer(v.Name),
								Slug:     pointy.GetPointer(v.Slug),
								HasMedia: pointy.GetPointer(uint32(v.HasImage)),
								Options:  opts,
							})
						}
					}
				}
			}
		}
	}

	productVariationEntityName := model.RpcEntityNames.ProductVariation
	var variations []*product.ProductVariationInfo
	if _, ok := subEntitySettingMap[productVariationEntityName]; ok {
		productVariationRepositoryName := model.RepositoryNames.ProductVariation
		subPreloadFields, hasSubPreloadFields := preloadMap[productVariationEntityName]
		subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[productVariationRepositoryName]
		if hasSubPreloadFields && hasSubColumnMap {
			subSelectCols := modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.ProductVariationColumns.ProductID] = map[string][]any{
				"equalTo": {pid},
			}
			subFilterMap[model.ProductVariationColumns.Status] = map[string][]any{
				"equalTo": {globalkey.StatusEnabled},
			}
			var orderBy string = model.ProductVariationColumns.ProductVariationID
			list, _, err := l.svcCtx.CurrentProduct.GetVariationList(l.ctx, model.M{
				"orderBy": orderBy,
				"select":  strings.Join(subSelectCols, ","),
			}, -1, subFilterMap)
			if err != nil {
				return nil, err
			}

			if len(list) > 0 {
				var ids []any
				for _, v := range list {
					ids = append(ids, v.ProductVariationID)
				}

				productVariationMetaEntityName := model.RpcEntityNames.ProductVariationMeta
				idVariationMetaMap := make(map[uint64]map[string]*model.ProductVariationMetum)
				if entitySettingMap, ok := subEntitySettingMap[productVariationMetaEntityName]; ok {
					if subPreloadFields, ok := preloadMap[productVariationMetaEntityName]; ok {
						var metaKeys []any
						metaKeyMap := make(map[string]string)
						for _, field := range subPreloadFields {
							if fieldSetting, ok := entitySettingMap[field]; ok && len(fieldSetting.Name) > 0 {
								metaKeys = append(metaKeys, fieldSetting.Name)
								metaKeyMap[fieldSetting.Name] = field
							}
						}
						if len(metaKeys) > 0 {
							metaFilterMap := make(map[string]map[string][]any)
							metaFilterMap[model.ProductVariationMetumColumns.ProductID] = map[string][]any{
								"in": ids,
							}
							metaFilterMap[model.ProductVariationMetumColumns.MetaKey] = map[string][]any{
								"in": metaKeys,
							}
							meta, _, err := l.svcCtx.CurrentProduct.GetVariationMeta(l.ctx, model.M{
								"orderBy": model.ProductVariationMetumColumns.MetaID,
							}, -1, metaFilterMap)
							if err != nil {
								return nil, err
							}

							if len(meta) > 0 {
								for _, v := range meta {
									key := v.MetaKey
									if len(key) > 0 && v.MetaValue.Valid {
										if field, ok := metaKeyMap[key]; ok {
											if _, ok := idVariationMetaMap[v.ProductID]; !ok {
												idVariationMetaMap[v.ProductID] = make(map[string]*model.ProductVariationMetum)
											}
											idVariationMetaMap[v.ProductID][field] = v
										}
									}
								}
							}
						}
					}
				}

				for _, v := range list {
					var skuKey *string
					var skuStr *string
					var mediaId *string
					var mediaUrl *string
					var inventory *uint32
					var saleCount uint32
					if metaMap, ok := idVariationMetaMap[v.ProductVariationID]; ok {
						for key, metum := range metaMap {
							val := metum.MetaValue.String
							switch key {
							case "stock":
								if i, err := strconv.ParseInt(val, 10, 32); err == nil {
									inventory = pointy.GetPointer(uint32(i))
								}
							case "saleCount": // 预设的总销量
								if i, err := strconv.ParseInt(val, 10, 32); err == nil {
									saleCount += uint32(i)
								}
							case "totalSaleCount": // 实际的总销量
								if i, err := strconv.ParseInt(val, 10, 32); err == nil {
									saleCount += uint32(i)
								}
							case "mediaId":
								mediaId = pointy.GetPointer(val)
							case "mediaUrl":
								mediaUrl = pointy.GetPointer(val)
							case "sku":
								skuStr = pointy.GetPointer(val)
							case "skuKey":
								skuKey = pointy.GetPointer(val)
							}
						}
					}
					variations = append(variations, &product.ProductVariationInfo{
						Id:           &v.ProductVariationID,
						Key:          skuKey,
						Label:        skuStr,
						GemPrice:     pointy.GetPointer(moneyx.FormatGem(int64(v.GemPrice))),
						RetailPrice:  pointy.GetPointer(moneyx.FormatAmount(int64(v.RetailPrice))),
						CounterPrice: pointy.GetPointer(moneyx.FormatAmount(int64(v.CounterPrice))),
						Weight:       pointy.GetPointer(moneyx.FormatWeight(int64(v.Weight))),
						MediaId:      mediaId,
						MediaUrl:     mediaUrl,
						Inventory:    inventory,
						SaleCount:    pointy.GetPointer(saleCount),
						SkuType:      pointy.GetPointer(uint32(v.SkuType)), // Todo: 添加注释
						Status:       pointy.GetPointer(uint32(v.Status)),
					})
				}
			}
		}
	}

	productMetaEntityName := model.RpcEntityNames.ProductMeta
	var meta []*product.MetaInfo
	if entitySettingMap, ok := subEntitySettingMap[productMetaEntityName]; ok {
		if subPreloadFields, ok := preloadMap[productMetaEntityName]; ok {
			var metaKeys []any
			metaKeyMap := make(map[string]string)
			for _, field := range subPreloadFields {
				if fieldSetting, ok := entitySettingMap[field]; ok && len(fieldSetting.Name) > 0 {
					metaKeys = append(metaKeys, fieldSetting.Name)
					metaKeyMap[fieldSetting.Name] = field
				}
			}
			if len(metaKeys) > 0 {
				subFilterMap := make(map[string]map[string][]any)
				subFilterMap[model.ProductMetumColumns.ProductID] = map[string][]any{
					"equalTo": {pid},
				}
				subFilterMap[model.ProductMetumColumns.MetaKey] = map[string][]any{
					"in": metaKeys,
				}
				orderBy := model.ProductMetumColumns.MetaID + " DESC"
				list, _, err := l.svcCtx.CurrentProduct.GetMeta(l.ctx, model.M{
					"orderBy": orderBy,
				}, -1, subFilterMap)
				if err != nil {
					return nil, err
				}

				if len(list) > 0 {
					keyMap := make(map[string]struct{})
					for _, v := range list {
						key := v.MetaKey
						if len(key) > 0 && v.MetaValue.Valid {
							if _, ok := keyMap[key]; ok { // 防重复
								continue
							}

							keyMap[v.MetaKey] = struct{}{}

							if field, ok := metaKeyMap[key]; ok {
								meta = append(meta, &product.MetaInfo{
									Id:    &v.MetaID,
									Key:   pointy.GetPointer(field),
									Value: pointy.GetPointer(v.MetaValue.String),
								})
							}
						}
					}
				}
			}
		}
	}

	productMediaEntityName := model.RpcEntityNames.ProductMedia
	var media []*product.MediaInfo
	if _, ok := subEntitySettingMap[productMediaEntityName]; ok {
		productMediaRepositoryName := model.RepositoryNames.ProductMedia
		subPreloadFields, hasSubPreloadFields := preloadMap[productMediaEntityName]
		subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[productMediaRepositoryName]
		if hasSubPreloadFields && hasSubColumnMap {
			subSelectCols := modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.ProductAttachmentRelationshipColumns.ObjectID] = map[string][]any{
				"equalTo": {pid},
			}
			subFilterMap[model.ProductAttachmentRelationshipColumns.ObjectType] = map[string][]any{
				"equalTo": {globalkey.PostTypeProduct},
			}
			var orderBy string = model.ProductAttachmentRelationshipColumns.AttachmentThumbnail + " DESC," +
				model.ProductAttachmentRelationshipColumns.AttachmentOrder
			list, _, err := l.svcCtx.CurrentProduct.GetMedia(l.ctx, model.M{
				"orderBy": orderBy,
				"select":  strings.Join(subSelectCols, ","),
			}, -1, subFilterMap)
			if err != nil {
				return nil, err
			}

			if len(list) > 0 {
				for _, v := range list {
					media = append(media, &product.MediaInfo{
						Uid:       &v.AttachmentRelationshipID,
						Id:        pointy.GetPointer(strconv.FormatInt(int64(v.AttachmentID), 10)),
						Thumbnail: pointy.GetPointer(uint32(v.AttachmentThumbnail)),
						Order:     pointy.GetPointer(uint32(v.AttachmentOrder)),
						Uri:       &v.AttachmentSource,
						Url:       &v.AttachmentImageURL,
						AltText:   &v.AttachmentImageAlt,
						MetaData:  &v.AttachmentImageMeta,
					})
				}
			}
		}
	}

	return &product.ProductInfo{
		Id:          &goods.ID,
		ParentId:    &goods.ParentID,
		Title:       &goods.Title,
		Slug:        &goods.Slug,
		Excerpt:     &goods.Excerpt,
		Uri:         &goods.GUID,
		Content:     content,
		Tracing:     tracing,
		Status:      pointy.GetPointer(uint32(goods.Status)),
		CreatedAt:   pointy.GetPointer(modelx.FormatTime(goods.ProductDate, "")),
		UpdatedAt:   pointy.GetPointer(modelx.FormatTime(goods.ProductModifiedDate, "")),
		VirtualType: pointy.GetPointer(uint32(goods.VirtualType)),
		Sort:        pointy.GetPointer(uint32(goods.SortOrder)),
		CreatedBy:   &goods.ProductAuthorID,
		Categories:  categories,
		Tags:        tags,
		Relations:   entries,
		Meta:        meta,
		Media:       media,
		Attrs:       attrs,
		Specs:       specs,
		Skus:        variations,
	}, nil
}
